package com.base.monitor.framework.util;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

/**
 * 对表达式求值，支持加减乘除括号小数点，不支持负数
 */
public class ExpressionCalculateUtils {

    //优先级Map
    private static final Map<String, Integer> OP_PRIORITY_MAP = new HashMap<String, Integer>() {
        {
            put("(", 0);
            put("+", 3);
            put("-", 3);
            put("*", 4);
            put("/", 4);
            put(")", 10);
        }
    };

    public static double operationExpression(String expression) {
        Stack<String> opStack = new Stack<String>();         //运算符栈
        Stack<BigDecimal> numStack = new Stack<BigDecimal>();       //操作数栈
        StringBuilder numBuilder = new StringBuilder();     //当前数值的追加器

        for (int i = 0; i < expression.length(); i++) {
            char c = expression.charAt(i);
            if (c >= '0' && c <= '9' || c == '.') {          //如果是数值则加入追加器
                numBuilder.append(c);
            } else {                                          //如果是运算符
                if (numBuilder.length() > 0) {               //如果numBuilder有值说明里面已经有一个数值
                    numStack.push(new BigDecimal(numBuilder.toString()));     //把数值入运算符栈
                    numBuilder.delete(0, numBuilder.length());  //清空数值
                }
                //读取到的字符是运算符
                String op = String.valueOf(c);
                if (opStack.empty()) {    //如果操作数栈没有运算符
                    opStack.push(op);
                } else {
                    //如果是"("则直接入运算栈
                    if ("(".equals(op)) {
                        opStack.push(op);
                    } else if (")".equals(op)) {
                        //如果是")"则进行括号匹配运算括号内的表达式
                        while (!"(".equals(opStack.peek())) {
                            stackOperation(opStack, numStack);
                        }
                        opStack.pop();
                    } else {
                        //如果是运算符，需要对比当前运算符op和栈顶的运算符优先级。
                        do {
                            //比较当前运算符和栈顶运算符的优先级,如果nowOp和opStack栈顶元素相同或者低级，
                            // 则进行运算，直到nowOp高于opStack栈顶
                            if (judgmentPriority(op, opStack.peek())) {
                                stackOperation(opStack, numStack);
                                if (opStack.empty()) {
                                    opStack.push(op);
                                    break;
                                }
                            } else {
                                opStack.push(op);
                                break;
                            }
                        } while (!opStack.empty());
                    }
                }
            }
        }

        //表达式结束，追加器里面有值
        if (numBuilder.length() > 0) {
            numStack.push(new BigDecimal(numBuilder.toString()));
        }

        while (!opStack.empty()) {
            stackOperation(opStack, numStack);
        }
        return numStack.pop().doubleValue();
    }

    /**
     * 进行一次二元运算
     *
     * @param opStack 操作栈
     * @param numStack 数栈
     */
    private static void stackOperation(Stack<String> opStack, Stack<BigDecimal> numStack) {
        String opT = opStack.pop();              //栈顶运算符
        BigDecimal num2 = numStack.pop();       //第二个操作数
        BigDecimal num1 = numStack.pop();       //第一个操作数
        BigDecimal operationNum = oneOperation(opT, num1, num2);   //num1 op num2

        numStack.push(operationNum);            //把计算完的结果放入操作数栈
    }

    /**
     * 单次计算，计算为num1 op num2
     *
     * @param op   运算符
     * @param num1 第一个操作数
     * @param num2 第二个操作数
     * @return num1 op num2
     */
    private static BigDecimal oneOperation(String op, BigDecimal num1, BigDecimal num2) {
        BigDecimal result = new BigDecimal(0);
        switch (op) {
            case "+":
                result = num1.add(num2);
                break;
            case "-":
                result = num1.subtract(num2);
                break;
            case "*":
                result = num1.multiply(num2);
                break;
            case "/":
                result = num1.divide(num2);
                break;
            default:
                break;
        }
        return result;
    }


    /**
     * 比较运算符优先级
     *
     * @param op1 第一个运算符
     * @param op2 第二个运算符
     * @return op1比op2相同或低级则返回true，op1比op2高级则返回false
     */
    private static boolean judgmentPriority(String op1, String op2) {
        return (OP_PRIORITY_MAP.get(op1) - OP_PRIORITY_MAP.get(op2)) <= 0;
    }

}

